<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="coinfaces">

        <title>修复git目录树对象</title>

    <link rel="stylesheet" href="../../theme/css/style.css">
    <link rel="stylesheet" href="../../theme/css/pygments.css">



</head>

<body>
    <div class="content">
<h1>修复git目录树对象</h1>    <p>
        under
            <a href="../../tags/git/">git</a>
            <a href="../../tags/fix/">fix</a>
    </p>
    <p>
        in <a href="../../categories/tech/">tech</a>
    </p>
    <p>Published: 2016-07-26</p>


    <div class="section" id="id1">
<h2>故障</h2>
<p>git代码库一般都有备份，如果一个代码库里有对象文件损坏，可以从备份恢复，这次故障发生的情况比较罕见，在push到裸代码库的时候发现有一个对象文件坏了。主要怪我太懒，代码库在U盘里放了太长的时间。</p>
</div>
<div class="section" id="id2">
<h2>修复过程</h2>
<p>先将原来的代码库复制一份，以免修复操作引入新的损坏，<tt class="docutils literal">git log</tt>命令执行正常，说明所有的commit对象都是好的，那损坏的最有可能是tree或blob对象，分两步定位损坏的对象。</p>
<ul>
<li><p class="first">先找到有问题的commit</p>
<div class="highlight"><pre><span></span><span class="k">for</span> commit in <span class="sb">`</span>git log --pretty<span class="o">=</span><span class="s1">&#39;%h&#39;</span><span class="sb">`</span><span class="p">;</span> <span class="k">do</span>
    git show <span class="nv">$commit</span> &gt;../<span class="nv">$commit</span>.patch
<span class="k">done</span>
</pre></div>
</li>
<li><p class="first">排查该commit引用的对象</p>
<div class="highlight"><pre><span></span>git ls-tree -t -r COMMIT_HASH <span class="p">|</span> grep BROKEN_HASH
</pre></div>
<p>这里<tt class="docutils literal">git <span class="pre">ls-tree</span></tt>报错，说明是子目录树对象损坏，否则grep命令会找到损坏的blob对象</p>
</li>
</ul>
<p>接下来的事情有点靠运气，用<tt class="docutils literal">git <span class="pre">cat-file</span> <span class="pre">-p</span></tt>查看这个commit前后的对应子目录树对象的内容，推测该目录树对象的可能内容，这一步的完成情况将直接决定最终的修复效果。在我的例子里，发现该commit和下一个commit连续修改了同一个源文件，好在这个commit比较简单，根据commit消息大概能推测出这个commit作了什么修改，于是分两步验证我的猜测：</p>
<ul class="simple">
<li>检出上一个commit的目录树，修改对应的源文件，用<tt class="docutils literal">cat SOURCE_FILE | git <span class="pre">hash-object</span> <span class="pre">--stdin</span></tt>查看修改后源文件的hash（注意<tt class="docutils literal">git <span class="pre">hash-object</span></tt>命令不要加<tt class="docutils literal"><span class="pre">-w</span></tt>参数），用<tt class="docutils literal">git <span class="pre">cat-file</span> <span class="pre">-p</span> PRINTED_HASH</tt>检查这个对象是否已经存在。</li>
<li>接下来用<tt class="docutils literal">git <span class="pre">ls-tree</span> PREV_TREE | sed 's/OLD_HASH/PRINTED_HASH/' | git mktree</tt>尝试生成一个新的tree对象，看打印在终端上的hash和损坏的对象的hash是否相同。</li>
</ul>
<p>在我的例子里这两步验证都通过了，运行<tt class="docutils literal">git fsck</tt>检查，发现代码库完好如初，将<tt class="docutils literal">git mktree</tt>生成的对象文件复制到原代码库即可。</p>
</div>
<div class="section" id="id3">
<h2>后记</h2>
<ul class="simple">
<li>目录树对象相对而言比较容易修复，因为它夹在commit对象和blob对象之间，有很多信息可以推测其内容，blob对象稍麻烦一些，如果commit习惯比较好（commit比较小，并且commit消息靠谱），修复的可能性也比较大，如果commit对象损坏了，就很难修复，因为推测commit时间比较困难。</li>
<li><tt class="docutils literal">git <span class="pre">cat-file</span> <span class="pre">-p</span></tt>和<tt class="docutils literal">git <span class="pre">ls-tree</span></tt>都可以查看tree对象，并且都可以作为<tt class="docutils literal">git mktree</tt>命令的输入，后者使用更方便。</li>
<li>在电子书Pro Git的Git Internals那一章原来有<tt class="docutils literal">git <span class="pre">mk-tree</span></tt>的例子，后来这部分内容旧改为使用<tt class="docutils literal">git <span class="pre">write-tree</span></tt>了，网上搜到旧版翻译里还能看到这个例子。</li>
<li><tt class="docutils literal">git <span class="pre">ls-tree</span></tt>的输出中含有tab字符，有的终端会自动将tab替换成空格，因此我一开始直接修改<tt class="docutils literal">git <span class="pre">ls-tree</span></tt>的输出，用here document的形式提供给<tt class="docutils literal">git mktree</tt>失败，建议用管道或重定向来准备<tt class="docutils literal">git mktree</tt>的输入，用<tt class="docutils literal">git help <span class="pre">ls-tree</span></tt>命令可以查看具体的输出格式。</li>
</ul>
<p>（完）</p>
</div>

    </div>
<nav class="nav sidebar">
    <div class="siteinfo">
        <div class="sitename"><a href="../..">coinfaces</a></div>
        <div class="tagline">Another Side of Life</div>
    </div>

        <a href="../../categories/">categories</a>
        <a href="../..">&larr; Home</a>
</nav><footer class="footer">
    <span>
        &copy; jpli &middot;
        under <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">CC-BY-SA 4.0</a>
    </span>
    |
    <span>
        Powered by:
        <a href="http://blog.getpelican.com/">Pelican</a>
    </span>
</footer></body>
</html>